/**************************************************************************************
 *  Copyright (c)  2010  Leszek Karcz.
 *  Permission is granted to copy, distribute and/or modify this document
 *  under the terms of the GNU Free Documentation License, Version 1.3
 *  or any later version published by the Free Software Foundation;
 *  with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts.
 *  A copy of the license is included in the section entitled "GNU
 *  Free Documentation License". 
 **************************************************************************************/
package data.tileables;
import data.LinkedList;
import data.Map;
import data.Player;
import data.UnitOwner;
import data.missions.*;

/**************************************************************************************
 * Author(s): Leszek Karcz
 * Rally point, contains army and handles unit movement, orders and attacks
 **************************************************************************************/
public class Rally extends Unit implements UnitOwner
{
	static public final int[] PRICE = { 0,0,0};
	private int workers;
	private int direction; //01 - up; 1 - up-right; 2 - down-right; 3 - down; 4 - down-left; 5 - up-left
	private LinkedList<Unit> combatArmy = new LinkedList<Unit>();
	private LinkedList<Unit> supportArmy = new LinkedList<Unit>();
	private boolean prospect, empty;
	private int unitCount;	
	
	
	/**************************************************************************************
	 * Author(s): Leszek Karcz
	 * Unit constructor
	 **************************************************************************************/
	public Rally(int ID, Player player)
	{
		super(ID, 0, player);
		this.setType("Rally");
		this.setImage("rally_dir");
		this.setIcon("rally_dir");
		this.setDisplayLevel(1);
		this.setDirection(0);
		this.setNumberOfCommands(5);
		
		
		this.prospect = false;
		this.empty = true;
		this.workers = 0;
		this.unitCount = 0;
		this.empty = true;
	}	
	
	/**************************************************************************************
	 * Author(s): Eric Martin-Taylor
	 * Called at the end of each tick.
	 * Will update the state, decrement allocations, damage units accordingly
	 * and update state again incase someone died
	 **************************************************************************************/
	public void execute(){
		update();

		allocate();

		if(!missionQueue.isEmpty())
		{
			int flag = missionQueue.peekFirst().execute();
			if(flag == 1)
			{
				missionQueue.removeFirst();
			}
			else if(flag == 2)
			{
				System.out.println("flag = 2");
				if(missionQueue.getSize() > 1)
				{
					missionQueue.removeFirst();
				}
			}
		}
		
		update();
	}
	/**************************************************************************************
	 * Author(s): Leszek Karcz
	 * returns the direction the unit is facing
	 **************************************************************************************/
	public int getDirection()
	{
		return this.direction;
	}

	
	/**************************************************************************************
	 * Author(s): Leszek Karcz
	 * sets the direction the unit is facing
	 **************************************************************************************/
	public void setDirection(int direction)
	{
		if((direction>=0) && (direction<=5))
		{
			this.direction=direction;
			if(combatArmy != null)
			{
				for(int i=0; i<combatArmy.getSize(); i++)
				{
					combatArmy.peekIndex(i).setDirection(direction);
				}
			}
		}
	}
	
	/**************************************************************************************
	 * Author(s): Leszek Karcz
	 * get image file name for the structure according to its level of completion
	 **************************************************************************************/
	public String getImage()
	{
		return "rally_dir_"+this.getDirection();
	}
	
	/**************************************************************************************
	 * Author(s): Leszek Karcz
	 * get image file name for the structure according to its level of completion
	 **************************************************************************************/
	public String getIcon()
	{
		return "rally_"+this.getPlayerID();
	}
	
	/**************************************************************************************
	 * Author(s): Leszek Karcz
	 * get icon file  name, highlight file name
	 **************************************************************************************/
	public String[] getImageData(int playerID)
	{
		return new String[]{((this.getPlayerID()==playerID) && (this.equals(this.player.getSelected())) ? "pselected_rally" : "p"+this.getPlayerID()+"_rally"),this.getImage(),""+this.getHealthPercentage()};
	}
	
	/**************************************************************************************
	 * Author(s): Leszek Karcz
	 * get icon file  name, highlight file name
	 **************************************************************************************/
	public String[] getIconData(int playerID)
	{
		return new String[]{"",((this.getPlayerID()==playerID) && (this.equals(this.player.getSelected())) ? "rally_selected" : "rally_"+this.getPlayerID()),""+this.getHealthPercentage()};
	}


	/**************************************************************************************
	 * Author(s): Changmin Bae
	 * Commands list for rally
	 **************************************************************************************/
	public String[] getCommandsText()
{
		if(this.getCommandMode()==0)
		{			
			return new String[]{"Move to Cursor","Pick Up Workers","Drop Off Workers","Build","Attack"};
		}
		else if(this.getCommandMode()==1)
		{			
			return new String[]{"Farm", "Fort", "Laboratory", "Power Plant", "Smelter", "WatchTower"};
		}
		else if(this.getCommandMode()==2)
		{			
			return new String[]{"N", "NE", "SE", "S", "SW", "NW"};
		}
		else
		{
			 return new String[]{"---"};
		}
	}

	/**************************************************************************************
	 * Author(s): Changmin Bae
	 * Commands
	 * 0 - Move to Cursor
	 * 1 - Pick Up Workers
	 * 2 - Drop Off Workers
	 * 3 - Build Menu
	 * Build Menu Commands
	 * 0 - Farm
	 * 1 - Fort
	 * 2 - Laboratory
	 * 3 - PowerPlant
	 * 4 - Smelter
	 * 5 - WatchTower
	 **************************************************************************************/
	public boolean enqueueSelectedCommand(String mode, Map m, int x, int y) 
	{
		if((this.getCommandMode()==0))
		{
			switch(this.getSelectedCommand())
			{			
			case 0:
				this.addMission(new MoveToCursor(this, m, player, x, y, this.getMovementTicks()));
				for(int i = 0; i< combatArmy.getSize(); i++)
					combatArmy.peekIndex(i).addMission(new MoveToCursor(combatArmy.peekIndex(i), m, player, x, y, this.getMovementTicks() ));
				break;
			case 1:
				if(mode != "Issue")
				{
					this.addMission(new PickUpWorkers(this, m, Integer.parseInt(mode)));
				}
				break;
			case 2:
				if(mode != "Issue")
				{
					this.addMission(new DropOffWorkers(this, m, Integer.parseInt(mode)));
				}				
				break;
			case 3:
				this.setCommandMode(1);	
				setNumberOfCommands(6);
				break;
			case 4:
				this.setCommandMode(2);
				setNumberOfCommands(6);
				break;
			}				
		}
		else if((this.getCommandMode()==1))
		{
			if(mode == "Issue")
			{
				this.setCommandMode(0);
				setNumberOfCommands(5);
			}
			switch(this.getSelectedCommand())
			{
			case 0:
				if(mode != "Issue")
				{
					this.addMission(new BuildFarm(this, Integer.parseInt(mode)));
				}	
				break;
			case 1:
				if(mode != "Issue")
				{
					this.addMission(new BuildFort(this, Integer.parseInt(mode)));
				}	
				break;
			case 2:
				if(mode != "Issue")
				{
					this.addMission(new BuildLaboratory(this, Integer.parseInt(mode)));
				}	
				break;
			case 3:
				if(mode != "Issue")
				{
					this.addMission(new BuildPowerPlant(this, Integer.parseInt(mode)));
				}		
				break;
			case 4:
				if(mode != "Issue")
				{
					this.addMission(new BuildSmelter(this, Integer.parseInt(mode)));
				}	
				break;
			case 5:
				if(mode != "Issue")
				{
					this.addMission(new BuildWatchTower(this, Integer.parseInt(mode)));
				}	
				break;
			}				
		}
		else if((this.getCommandMode()==2))
		{
			switch(this.getSelectedCommand())
			{
			case 0:
				this.addMission(new Attack(this, player, 0));
				this.setCommandMode(0);
				this.setNumberOfCommands(5);
				break;
			case 1:
				this.addMission(new Attack(this, player, 1));
				this.setCommandMode(0);
				this.setNumberOfCommands(5);
				break;
			case 2:
				this.addMission(new Attack(this, player, 2));
				this.setCommandMode(0);
				this.setNumberOfCommands(5);
				break;
			case 3:
				this.addMission(new Attack(this, player, 3));
				this.setCommandMode(0);
				this.setNumberOfCommands(5);
				break;	
			case 4:
				this.addMission(new Attack(this, player, 4));
				this.setCommandMode(0);
				this.setNumberOfCommands(5);
				break;	
			case 5:
				this.addMission(new Attack(this, player, 5));
				this.setCommandMode(0);
				this.setNumberOfCommands(5);
				break;	
			}	
		}
		else
		{
			switch(this.getSelectedCommand())
			{			
				case 0:
					this.die();
			}
		}		
		return true;
	}
	
	/**************************************************************************************
	 * Author(s): Changmin Bae
	 * Gets movement ticks of slowest unit inside Rally
	 **************************************************************************************/
	public int getMovementTicks()
	{
		int toReturn = 1;
		int x = combatArmy.getSize();
		for(int i=0; i<x; i++)
		{
			toReturn = Math.max(toReturn, combatArmy.peekIndex(i).getMovementTicks());
		}
		return toReturn;
	}
	
	
	/**************************************************************************************
	 * Author(s): Eric Martin-Taylor
	 * Adds a unit to the back of the list
	 **************************************************************************************/
	public boolean addUnit(Unit unit){		
		unit.removeOwner();		
		if((unit.getX()==this.getX()) && (unit.getY()==this.getY()) && (unit.getState()!=0))
		{
			this.supportArmy.remove(unit); //remove from support
			if(!this.combatArmy.find(unit)) // put into combat if not there
			{
				System.out.println("Trying to add to combat army");
				this.combatArmy.addBack(unit);
			}
		}
		else
		{
			this.combatArmy.remove(unit); //remove from combat
			if(!this.supportArmy.find(unit)) // put into support if not there
			{
				this.supportArmy.addBack(unit);
			}
		}
		
		unit.setOwner(this);
		update();		
		return true;
	}
		
	
	/**************************************************************************************
	 * Author(s): Eric Martin-Taylor
	 * Removes a unit from the list.
	 * 		True = unit was removed
	 * 		False = unit was in in rally
	 **************************************************************************************/
	public boolean removeUnit(Unit unit){
		
		if(combatArmy.remove(unit) == null)
			return false;
		
		update();
		
		return true;
	}
	
	/**************************************************************************************
	 * Author(s): Eric Martin-Taylor	 
	 * Removes workers from the rally.
	 * 		The number returned is the amount of workers removed
	 **************************************************************************************/
	public int removeWorker(int amount){
		
		int temp = 0;
		if(workers-amount < 0){
			temp = workers;
			workers = 0;
			return temp;
		}
			
		workers -= amount;
		return amount;
	}
	
	/**************************************************************************************
	 * Author(s): Eric Martin-Taylor
	 * Adds workers to the rally.
	 **************************************************************************************/
	public boolean addWorker(int amount){
		workers+=amount;
		return true;
	}
	
	/**************************************************************************************
	 * Author(s): Eric Martin-Taylor
	 * Returns the combined attack of everyone in the rally
	 **************************************************************************************/
	public int attack(){return this.getStats()[3];}
	
	/**************************************************************************************
	 * Author(s): Eric Martin-Taylor
	 * Spreads out all the Damage amoung the troops evenly
	 * Any untaken damage will simply disappear and not rollover
	 **************************************************************************************/
	public void changeHealth(double amount){
		
		double damage = amount / unitCount;
		
		for(int i = 0; i<unitCount; i++)
			(combatArmy.peekIndex(i)).changeHealth(damage);
		
		update();
	}
	
	/**************************************************************************************
	 * Author(s): Eric Martin-Taylor
	 * The current state of prospecting
	 **************************************************************************************/
	public boolean isProspecting(){return prospect;	}
	
	/**************************************************************************************
	 * Author(s): Eric Martin-Taylor
	 * Updates the states of the army in reflection to who and what is in it
	 **************************************************************************************/
	public void update(){
		
		unitCount=this.supportArmy.getSize()+this.combatArmy.getSize();
		
		int i=0;
		int tempOffence=0, tempDefence=0, tempHealth=0, tempMaxHealth=0, tempVision=0, tempArmor=0;
		int move=0;
		
		this.prospect = false;
		
		int []tempStats = this.getStats();
		
		for(i = 0; i<tempStats.length; i++ )
			tempStats[i] = 0;
		
		Unit temp;
		
		for(i =0; i< combatArmy.getSize(); i++){
			
			temp = combatArmy.peekIndex(i);
			
			tempStats = temp.getStats();
			
			tempHealth += tempStats[0];
			tempMaxHealth += tempStats[1];
			tempArmor += tempStats[2];
			tempOffence += tempStats[3];
			tempDefence += tempStats[4];
			tempVision = Math.max(tempStats[5], tempVision);

			move = Math.max(move, temp.getMovementTicks());
			
			if(temp instanceof Explorer)
				if(((Explorer)(temp)).isProspecting())
					this.prospect = true;
				
			}
		
		this.setStats(tempHealth, tempMaxHealth, tempArmor, tempOffence, tempDefence, tempVision);
		this.setMovementTicks(move);
		
		unitCount = combatArmy.getSize();
		
		if(unitCount == 0){
			empty = true;
		}
		else
			empty = false;

	}
	
	/**************************************************************************************
	 * Author(s): Eric Martin-Taylor
	 * Kills off everyone, then kills itself.
	 * This is meant to be called by the Player or Item classes, or something else.
	 * The rally should also cease to exist
	 **************************************************************************************/
	public void die(){
		//TODO finish adding die method
		this.combatArmy.setToFirst();
		for(int i = 0; i<this.combatArmy.getSize(); i++)
		{
			combatArmy.peekIndex(i).die();
		}
		this.clearMissions();
		
		for(int i = 0; i<supportArmy.getSize(); i++)
		{
			Unit support = supportArmy.peekIndex(i);
			support.removeOwner();
			support.clearMissions();
		}
		
		super.die();
		player.removeRally(this);
	}
	
	/**************************************************************************************
	 * Author(s): Eric Martin-Taylor
	 * Turn prospecting on
	 **************************************************************************************/
	public boolean startProspect(){
		
		prospect = false;
		Unit temp;
		
		for(int i =0; i< unitCount; i++){
			temp = combatArmy.peekIndex(i);
				if(temp instanceof Explorer){
					((Explorer)(temp)).startProspecting();
					this.prospect = true;
				}
		}
		
		return prospect;
	}

	public void executeSelectedCommand() {
		// TODO Auto-generated method stub
		
	}
	
	/**************************************************************************************
	 * Author(s): Eric Martin-Taylor
	 * Turn prospecting off
	 **************************************************************************************/
	public void stopProspect(){
		
		prospect = false;
		Unit temp;
		for(int i =0; i< unitCount; i++){
			temp = combatArmy.peekIndex(i);
			if(temp instanceof Explorer)
					((Explorer)(temp)).stopProspecting();
		}
	}
	
	/**************************************************************************************
	 * Author(s): Eric Martin-Taylor
	 * Build a building and pass it to the Player class
	 * ALso sets the lock "building"
	 **************************************************************************************/
	public void buildFarm(int ID, Player player, int workerCount){
		
		if(workerCount > workers)
			workerCount = workers;
		
		if(player.createFarm(this.getX(), this.getY(), workerCount))
			workers -= workerCount;
		
	}
	
	/**************************************************************************************
	 * Author(s): Changmin Bae
	 * Integrating workers into the Rally's non-combat army
	 **************************************************************************************/
	public void dropOffWorker(int numWorkers, Structure s)
	{
		if(s.player == player)
		{
			if(numWorkers > workers)
			{
				numWorkers = workers;
			}
			s.addWorkers(numWorkers);
			this.removeWorker(workers);
		}
	}
	
	/**************************************************************************************
	 * Author(s): Changmin Bae
	 * Start building a structure at current location (if possible)
	 * type:
	 * 0 - Farm
	 * 1 - Fort
	 * 2 - Laboratory
	 * 3 - PowerPlant
	 * 4 - Smelter
	 * 5 - WatchTower
	 **************************************************************************************/
	public void startBuilding(int type, int numWorkers)
	{
		System.out.println("numworkers before = " + numWorkers);
		if(numWorkers > workers)
		{
			numWorkers = workers;
		}
		System.out.println("numworkers after = " + numWorkers);
		switch(type)
		{
		case 0:
			if(player.createFarm(getX(), getY(), numWorkers))
			{
				this.removeWorker(numWorkers);
			}			
			break;
		case 1:
			if(player.createFort(getX(), getY(), numWorkers))
			{
				this.removeWorker(numWorkers);
			}			
			break;
		case 2:
			if(player.createLaboratory(getX(), getY(), numWorkers))
			{
				this.removeWorker(numWorkers);
			}			
			break;
		case 3:
			if(player.createPowerPlant(getX(), getY(), numWorkers))
			{
				this.removeWorker(numWorkers);
			}			
			break;
		case 4:
			if(player.createSmelter(getX(), getY(), numWorkers))
			{
				this.removeWorker(numWorkers);
			}			
			break;
		case 5:
			if(player.createWatchTower(getX(), getY(), numWorkers))
			{
				this.removeWorker(numWorkers);
			}			
			break;		
		}
	}
	
	/**************************************************************************************
	 * Author(s): Changmin Bae
	 * Integrating workers into the Rally's non-combat army
	 **************************************************************************************/
	public void pickupWorker(int numWorkers, Structure s)
	{
		if(hasFighter())
		{
			int numUnassignedWorkers = s.getWorkerCount();
			if(numWorkers > numUnassignedWorkers)
			{
				numWorkers = numUnassignedWorkers;
			}
			
			s.removeWorkers(numWorkers);
			this.addWorker(numWorkers);
		}
	}
	
	/**************************************************************************************
	 * Author(s): Eric Martin-Taylor
	 * Disbanding the army
	 **************************************************************************************/
	public void disband(){
		for(int i = 0; combatArmy.isEmpty(); i++)
		{
			combatArmy.removeFirst().removeOwner();
		}
		//TODO reasign to base
		
		this.clearMissions();
		this.update();
		
	}
	public int getWorkers()
	{
		return workers;
	}
	/**************************************************************************************
	 * Author(s): Leszek Karcz
	 * returns if the army has a fighter unit
	 **************************************************************************************/
	public boolean hasFighter()
	{
		for(int i=0; i<this.combatArmy.getSize();i++)
		{
			if(this.combatArmy.peekIndex(i) instanceof FighterUnit)
			{
				return true;
			}
		}
		return false;
		
		
	}
	
	/**************************************************************************************
	 * Author(s): Leszek Karcz
	 * Return the stats text of units with applied area bonuses
	 **************************************************************************************/
	public String[] getStatsText()
	{
		int armorBonus=this.player.getAreaBonus(this.getX(), this.getY());
		int[] stats=this.getBaseStats();		
		return new String[]{"ID: "+this.getID()+"        size: "+this.combatArmy.getSize()+" "+(this.combatArmy.getSize()+this.supportArmy.getSize()),
							"Health: "+(int)stats[0]+"/"+stats[1],
							"Armor: "+stats[2],
							"Offensive Damage: "+stats[3],
							"Defensive Damage: "+stats[4],
							"Movement: "+this.getMovementTicks(),
							"Vision: "+this.getVisionRadius(),
							"Upkeep: "+this.getUpkeep()[0]+" "+this.getUpkeep()[1]+" "+this.getUpkeep()[2],
							"State: "+this.getStateText(),
							"Workers: "+this.workers,
							"Mission: "+this.getCurrentMissionName()};
	}
	
	/**************************************************************************************
	 * Author(s): Kyle Van Wie
	 * returns the combatArmy
	 **************************************************************************************/
	public LinkedList<Unit> getCombatArmy(){
		return combatArmy;
	}
	/**************************************************************************************
	 * Author(s): Kyle Van Wie
	 * returns the supportArmy
	 **************************************************************************************/
	public LinkedList<Unit> getSupportArmy(){
		return supportArmy;
	}
	

	
	public int [] getUpkeep(){
		
		int[] resourcesNeeded = new int[3];
		
		for(int i = 0; i< combatArmy.getSize(); i++){
			int [] temp = combatArmy.peekIndex(i).getUpkeep();
			resourcesNeeded[FOOD] += temp[FOOD];
			resourcesNeeded[METAL] += temp[METAL];
			resourcesNeeded[ENERGY] += temp[ENERGY];
		}
		return resourcesNeeded;
	}
}
